<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Base\Order;
use App\Models\GameLog\LogPayRecord;
use App\Models\Game\PlayerGamedata;
use App\Service\PlayerService;
use App\Service\RequestService;
use Illuminate\Support\Facades\Log;

/**
 * 支付.
 */
class PayBtController extends Controller
{
    const SUCCESS = 'success';
    const FAIL = 'fail';
    const QUICK_SUCCESS = 'SUCCESS';
    const QUICK_FAIL = 'FAIL';
    const SIGN_ERROR = 'sign_data_verify_failed';
    const ENCRYP_ERROR = 'encryp_data_decrypt_failed';
    const AREA_ERROR = 'failed:game_area error';
    const ORDER_ERROR = 'failed:game_orderid error';
    const ROLE_ERROR = 'failed:game_role_id error';
    const PRICE_ERROR = 'failed:pay_price error';
    const OTHER_ERROR = 'failed:order error';

    /**
     * 安卓回调地址
     */
    public function shipmentsAndroid()
    {
        $callbackKey = '28469102399592977704774049719761';
        $md5Key = 'daoakqnfkjdfwbofxp6g6xgwwodt8kel';
        return $this->quickDeliverGood($callbackKey,$md5Key);
    }

    /**
     * IOS回调地址
     */
    public function shipmentsIos()
    {
        $callbackKey = '29937227710411538497164924426929';
        $md5Key = 'vfhivwvcgwhqff9ui4iogiov7i9oeoma';
        return $this->quickDeliverGood($callbackKey,$md5Key);
    }

    /**
     * 支付回调发货
     * @param $callbackKey
     * @param $md5Key
     * @return string
     * @doc https://www.quicksdk.com/doc-0.html?cid=27&tdsourcetag=s_pctim_aiomsg
     */
    private function quickDeliverGood($callbackKey,$md5Key)
    {
        $params = $params = request()->all();
        if (null == $params) {
            return self::QUICK_FAIL;
        }
        Log::warning('收到支付回调:', $params);
        //验证签名
        if (false == $this->verifyQuickSign($params, $md5Key)) {
            return self::QUICK_FAIL;
        }
        $nt_data_decode = $this->decode($params['nt_data'], $callbackKey);
        Log::warning('解密nt_data:', [$nt_data_decode]);
        $nt_data = xmlToArray($nt_data_decode);
        $nt_data = $nt_data['message'];
        //游戏方订单号
        $order_sn = $nt_data['game_order'];
        $extras_params = $nt_data['extras_params'];
        $extras = explode('*', $extras_params);
        $server_id = $extras[1];

        if (1 == $nt_data['status']) {
            Log::error('参数状态未支付!');

            return self::QUICK_FAIL;
        }

        //判断是否充值过
        $one = $this->is_pay($order_sn);
        if (false === $one) {
            return self::QUICK_SUCCESS;
        }
        //查询游戏订单号
        $order = $this->gameOrder($server_id, $order_sn);
        if (false == $order) {
            return self::ORDER_ERROR;
        }
        $playerData = PlayerService::getPayerInfo($server_id, $order->playerid);

        $ret = $this->exePay($order->count, $nt_data['amount'], $one, $order, $playerData,
            $server_id, $order_sn, $nt_data['order_no']);
        if ($ret) {
            return self::QUICK_SUCCESS;
        } else {
            Log::error('更新状态失败!');

            return self::QUICK_FAIL;
        }
    }

    /** 判断是否充值过
     * @param $order_sn
     *
     * @return bool|Order
     */
    private function is_pay($order_sn)
    {
        $one = Order::where('order_sn', $order_sn)->first();
        if ($one && 1 == $one->status) {
            Log::warning('订单已存在并支付');

            return false;
        } else {
            return $one;
        }
    }

    /**
     * 执行支付发货.
     *
     * @param $count
     * @param $amount
     * @param Order        $one
     * @param LogPayRecord $order
     * @param $playerData
     * @param $server_id
     * @param $order_sn
     * @param $plat_order
     *
     * @return bool
     */
    private function exePay($count, $amount, $one, LogPayRecord $order, $playerData, $server_id, $order_sn, $plat_order)
    {
        //判断金额
        if ($count != $amount) {
            Log::error('金额不一致!');

            return false;
        }
        //写入本地订单表
        if (null == $one) {
            $local = $this->addOrder($order, $playerData, $playerData->channelId, $server_id, $order_sn, $plat_order);
            if (!$local) {
                Log::error('写入本地失败!');

                return false;
            }
        }
        //通知游戏服
        $send = RequestService::sendOrderPay($server_id, $order_sn, $order->platformId,
            $order->playerid, $order->accountId);
        if (!$send) {
            Log::error('发送给游戏服失败!');

            return false;
        }
        //更新订单状态
        if ($one) {
            $one->status = 1;
            $ret = $one->save();
        } else {
            $local->status = 1;
            $ret = $local->save();
        }

        return $ret;
    }

    /**
     * 验证支付签名.
     *
     * @param $post
     * @param string $app_key
     *
     * @return bool
     */
    protected function verfiy($post, $app_key)
    {
        $sign = $post['sign'];
        $data = array_except($post, 'sign');
        $verify_sign = verifySign($data, $app_key);
        //验证签名
        if (0 !== strcasecmp($sign, $verify_sign)) {
            return false;
        }

        return true;
    }

    /**
     * 写入订单记录.
     *
     * @param LogPayRecord   $order
     * @param PlayerGamedata $player
     * @param $channlId
     * @param $server_id
     * @param $order_sn
     * @param $tcd
     *
     * @return Order
     */
    protected function addOrder(LogPayRecord $order, $player, $channlId, $server_id, $order_sn, $tcd)
    {
        $localOrder = new Order();
        $localOrder->order_sn = "{$order_sn}";
        $localOrder->channel_id = $channlId;
        $localOrder->userid = "{$order->userid}";
        $localOrder->player_id = "{$order->playerid}";
        $localOrder->amount = $order->count;
        $localOrder->status = 0;
        $localOrder->order_time = $order->time;
        $localOrder->player_name = $player->name;
        $localOrder->userid = $player->userId;
        $localOrder->platform_id = $order->platformId;
        $localOrder->server_id = $server_id;
        $localOrder->platform_order_sn = $tcd;
        $localOrder->pay_time = time();
        $localOrder->charge_level = $order->accountId;
        $localOrder->save();

        return $localOrder;
    }

    /**
     * 查询游戏服订单记录.
     *
     * @param $server_id
     * @param $order_sn
     *
     * @return bool
     */
    protected function gameOrder($server_id, $order_sn)
    {
        if (!setDB($server_id)) {
            return false;
        }
        $record = LogPayRecord::where('orderId', $order_sn)->first();
        if (!$record) {
            Log::error('订单不存在:', ['server_id' => $server_id, 'order_sn' => $order_sn]);

            return false;
        } else {
            return $record;
        }
    }

    /**
     * quickIos签名验证
     *
     * @param $params
     * @param $md5Key
     *
     * @return bool
     */
    private function verifyQuickSign($params, $md5Key)
    {
        $localMd5Sign = md5($params['nt_data'].$params['sign'].$md5Key);
        if (0 !== strcasecmp($params['md5Sign'], $localMd5Sign)) {
            return false;
        }

        return true;
    }

    /**
     * 解密方法
     * $strEncode 密文
     * $keys 解密密钥 为游戏接入时分配的 callback_key.
     */
    private function decode($strEncode, $keys)
    {
        if (empty($strEncode)) {
            return $strEncode;
        }
        preg_match_all('(\d+)', $strEncode, $list);
        $list = $list[0];
        if (count($list) > 0) {
            $keys = self::getBytes($keys);
            for ($i = 0; $i < count($list); ++$i) {
                $keyVar = $keys[$i % count($keys)];
                $data[$i] = $list[$i] - (0xff & $keyVar);
            }

            return self::toStr($data);
        } else {
            return $strEncode;
        }
    }

    /**
     * MD5签名替换.
     */
    private static function replaceMD5($md5)
    {
        strtolower($md5);
        $bytes = self::getBytes($md5);

        $len = count($bytes);

        if ($len >= 23) {
            $change = $bytes[1];
            $bytes[1] = $bytes[13];
            $bytes[13] = $change;

            $change2 = $bytes[5];
            $bytes[5] = $bytes[17];
            $bytes[17] = $change2;

            $change3 = $bytes[7];
            $bytes[7] = $bytes[23];
            $bytes[23] = $change3;
        } else {
            return $md5;
        }

        return self::toStr($bytes);
    }

    /**
     * 转成字符数据.
     */
    private static function getBytes($string)
    {
        $bytes = [];
        for ($i = 0; $i < strlen($string); ++$i) {
            $bytes[] = ord($string[$i]);
        }

        return $bytes;
    }

    /**
     * 转化字符串.
     */
    private static function toStr($bytes)
    {
        $str = '';
        foreach ($bytes as $ch) {
            $str .= chr($ch);
        }

        return $str;
    }
}
